/*
 * numerics.c
 *
 * Copyright (C) 2011-2012 Dan Reidy <dubkat@ratbox.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 *
 *       Foundation, Inc.
 *       59 Temple Place - Suite 330
 *       Boston, MA  02111-1307, USA.
 *
 *
 *  Project Name: ratbox-monitor
 *    Created on: Dec 4, 2010
 *        Author: Daniel J Reidy <dubkat@ratbox.org>
 *            Id: $Id: numerics.c 21 2012-03-26 03:59:17Z dubkat $
 */

#include "ratbox_lib.h"
#include "libconfig.h"
#include "setup.h"
#include "uthash.h"
#include "numerics.h"
#include "common.h"
#include "worker.h"
#include "logger.h"
#include "openmp.h"

extern config_t conf;
extern struct my_clients *clients;
extern struct my_opers *opers;

// debugging notices
void
handle_UNKNOWN(struct conn *serv, int parc, char **parv)
{
	char string[1024];
	int i;

	rb_strlcpy(string, "UNKNOWN: ", sizeof(string));
	for(i = 0; i < parc; i++)
	{
		rb_snprintf_append(string, sizeof(string), "parv[%i](%s) ", i, parv[i]);
	}
	//rb_snprintf_append(string, sizeof(string), "\n");
	logger(DEBUG, string);
}

void
handle_001(struct conn *serv, int parc, char **parv)
{
	struct server *s = &my_server;
	struct status *me = &my_status;
	rb_strlcpy(me->currentnick, parv[2], sizeof(me->currentnick));
	rb_strlcpy(s->servername, parv[0], sizeof(s->servername));
	logger(DEBUG,
	       "handle_001: me->currentnick = \"%s\", s->servername = \"%s\"",
	       me->currentnick, s->servername);
}

void
handle_004(struct conn *serv, int parc, char **parv)
{
	struct server *s = &my_server;
	rb_strlcpy(s->version, parv[4], sizeof(s->version));
	rb_strlcpy(s->umodes, parv[5], sizeof(s->umodes));
}

// totally ugly ISUPPORT parsing
void
handle_005(struct conn *serv, int parc, char **parv)
{
	struct server *s = &my_server;

	int i;
	for(i = 3; i < parc; i++)
	{

		if(strchr(parv[i], '='))
		{
			static char key[15];
			static char val[15];
			char *k = key;
			char *v = val;

			// split this bitch up into a KEY => VALUE
			while(*(parv[i]) != '=')
				*k++ = *(parv[i])++;
			*k++ = '\0';

			*(parv[i])++;

			while(*(parv[i]))
				*v++ = *(parv[i])++;
			*v++ = '\0';

			if(!strcmp(key, "NETWORK"))
			{
				rb_strlcpy(s->network, val, sizeof(s->network));
				continue;
			}

			if(!strcmp(key, "NICKLEN"))
			{
				s->nicklen = atoi(val);
				continue;
			}

			if(!strcmp(key, "CHANNELLEN"))
			{
				s->chanlen = atoi(val);
				continue;
			}
		}

		if(!strcmp(parv[i], "ETRACE"))
		{
			s->etrace = YES;
			continue;
		}
		if(!strcmp(parv[i], "CPRIVMSG"))
		{
			s->cprivmsg = YES;
			continue;
		}
		if(!strcmp(parv[i], "CNOTICE"))
		{
			s->cnotice = YES;
			continue;
		}
		if(!strcmp(parv[i], "FNC"))
		{
			s->fnc = YES;
			continue;
		}

	}

}

// end of MOTD, get to work!
void
handle_376(struct conn *serv, int parc, char **parv)
{
	const char *oper;
	const char *pass;
	const char *away;

	struct server *s = &my_server;
	struct status *me = &my_status;

	config_lookup_string(&conf, "irc.oper_name", &oper);
	config_lookup_string(&conf, "irc.oper_pass", &pass);
	config_lookup_string(&conf, "irc.away", &away);

	if(strlen(oper) && strlen(pass))
		sendto_one(serv, "OPER %s %s", oper, pass);

	if(strlen(away))
		sendto_one(serv, "AWAY :%s", away);

	logger(DEBUG,
	       "server: %s version(%s) etrace(%s) nicklen(%i) chanlen(%i) umodes(%s)",
	       s->servername, s->version, s->etrace ? "YES" : "NO", s->nicklen,
	       s->chanlen, s->umodes);

	logger(DEBUG, "Online as: %s on %s", me->currentnick, s->servername);

}

// opers
void
handle_243(struct conn *serv, int parc, char **parv)
{
	struct server *s = &my_server;
	//struct status *me = &my_status;
	struct my_opers *op = NULL;
	char *pos = NULL;

	// if it's not from my server, and not a stats O, i don't want it.
	if(irccmp(parv[0], s->servername) && *(parv[3]) != 'O')
		return;

	if((op = rb_malloc(sizeof(struct my_opers))) == NULL)
	{
		logger(CRIT, "ERROR: OUT OF MEMORY!");
		exit(EXIT_FAILURE);
	}

	op->id = new_id(NO);

	rb_strlcpy(op->opernick, parv[6], sizeof(op->opernick));
	rb_strlcpy(op->operhost, parv[4], sizeof(op->operhost));
	pos = strchr(parv[7], 'A');
	if(pos != NULL)
		op->admin = YES;
	else
		op->admin = NO;

	hash_add_oper(op);

	logger(DEBUG, "Adding Oper: %s (%s) admin: %s", op->opernick, op->operhost,
	       op->admin ? "YES" : "NO");
}

// zomg, you're an oper. fear. now try to set our usermodes based
// on what we need vs what's supported.
void
handle_381(struct conn *serv, int parc, char **parv)
{
	struct server *s = &my_server;
	struct status *me = &my_status;

	size_t sz = sizeof(char *) * strlen(s->umodes);
	char *c = s->umodes;
	char *mode = rb_malloc(sz);
	const char *extra = NULL;

	config_lookup_string(&conf, "irc.umodes", &extra);
	rb_strlcpy(mode, "-a+i", sizeof(mode));

	while(*c)
	{
		switch (*c)
		{
		case 'b':
			rb_snprintf_append(mode, sz, "b");	// flood notices
			break;
		case 'n':
			rb_snprintf_append(mode, sz, "n");	// nick changes
			break;
		case 'f':
			//rb_snprintf_append(mode, sz, "f");    // iline full might be usefull
			break;
		case 'y':
			rb_snprintf_append(mode, sz, "y");	// stats spying
			break;
		case 'z':
			rb_snprintf_append(mode, sz, "z");	// need operwall for for qurve lists
			break;
		case 'c':
			rb_snprintf_append(mode, sz, "c");	// legacy connect notices
			break;
		case 'C':
			s->hasExtendedInfo = YES;
			break;

		default:
			break;
		}
		*c++;
	}

	if(s->hasExtendedInfo)
		rb_snprintf_append(mode, sz, "C-c");

	if(!EmptyString(extra))
		rb_snprintf_append(mode, sz, extra);

	logger(DEBUG, "*** SETTING UMODES: %s :%s", me->currentnick, mode);
	sendto_one(serv, "MODE %s %s", me->currentnick, mode);
	rb_free(mode);

	// get our server operators before we do a TRACE
	sendto_one(serv, "STATS O");

	join_channels(serv);

	elapse(START);
	if(s->etrace)
	{
		if(s->hasExtendedInfo)
			sendto_one(serv, "ETRACE -full");
		else
			sendto_one(serv, "ETRACE");
	}
	else
		sendto_one(serv, "TRACE");

}

/* self failed oper attempt */
void
handle_491(struct conn *serv, int parc, char **parv)
{
	join_channels(serv);
	report_to_channel(serv, "ERROR -> Failed to oper up!");
	logger(INFO, "ERROR: Failed to oper up on server: %s", parv[0]);
}

void
handle_262(struct conn *serv, int parc, char **parv)
{
	struct my_clients *current_user, *tmp;
	const char *runtime = elapse(STOP);
	int hash_count = HASH_COUNT(clients);

	report_to_channel(serv,
			  "STARTUP -> %s-%s startup complete. Hashed %i clients in %.4lf seconds.",
			  PACKAGE_NAME, PACKAGE_VERSION, hash_count, atof(runtime));

	logger(INFO, "HASHED %i clients in %s seconds.", hash_count, runtime);

	/**
	 * now that we have our hash filled with clients, process the existing clients.
	 * a good idea would be to do these in order from less "stressfull" calculations
	 * up to the more intensive operations.
	 */
#	pragma omp master
	{
		HASH_ITER(hh, clients, current_user, tmp)
		{
			if(check_seedscore(current_user))
			{
				if(ban_client(serv, current_user, BAN_SEED))
					report_to_channel(serv,
							  "SEEDRAND -> Traced client %s scored: %i",
							  current_user->nick, current_user->seed);

			}
		}
	}

}

// nick in use, try to use our altnick
void
handle_433(struct conn *serv, int parc, char **parv)
{
	const char *altnick;
	config_lookup_string(&conf, "irc.alt_nick", &altnick);
	sendto_one(serv, "NICK %s", altnick);
	// XXX setup a nick-regain
}

void
handle_TRACE(struct conn *serv, int parc, char **parv)
{
	// etrace full: parc = 12
	// XXX for the moment, we only support ratbox-3 ETRACE -full

	struct my_clients *cli = NULL;

	if(parc == 12)
	{
		if((cli = malloc(sizeof(struct my_clients))) == NULL)
		{
			logger(CRIT, "ERROR: OUT OF MEMORY!");
			exit(EXIT_FAILURE);
		}

		rb_strlcpy(cli->class, parv[4], sizeof(cli->class));
		rb_strlcpy(cli->nick, parv[5], sizeof(cli->nick));
		rb_strlcpy(cli->user, parv[6], sizeof(cli->user));
		rb_strlcpy(cli->host, parv[7], sizeof(cli->host));
		rb_strlcpy(cli->ip, parv[8], sizeof(cli->ip));
		rb_strlcpy(cli->extra1, parv[9], sizeof(cli->extra1));
		rb_strlcpy(cli->extra2, parv[10], sizeof(cli->extra2));
		rb_strlcpy(cli->gecos, parv[11], sizeof(cli->gecos));

		cli->id = new_id(NO);
		cli->isSpoofed = NO;
		cli->seed = seedscore(cli->nick);

		if(!strcmp(cli->ip, "255.255.255.255"))
			cli->isSpoofed = YES;

		hash_add_client(cli);

	}
}
